#ifndef COLLISION_BOX_H_
#define COLLISION_BOX_H_

#include <GL/glew.h>

#include "util.h"
#include <cmath>
#include <vector>
#include <iostream>

enum 
{
	
	CBOX_QUAD = 1,
	CBOX_TRIANGLE
	
};


class CCollisionData;


//Just handles the collision box data
class CCollisionBox
{

private:
	
	bool m_bEnabled;
	
	int m_SpecialMapIndex; //for map tiles!

	Vector2D m_Pos;
	std::vector<Vector2D> m_Points;
	std::vector<Vector2D> m_Axis;
	std::vector<Vector2D> m_TestPoints;
	Vector2D m_Center;
	
	int m_CollisionType;
	
	void FindSATMinMax( std::vector< Vector2D > &, Vector2D, Vector2D *, double *, double * );
	
public:
	
	CCollisionBox() : m_CollisionType( CBOX_QUAD )
	{
		
		m_bEnabled = false;
		
		ResizeShape();
		
	}
	
	inline void SetCollisionType( int type )
	{
		
		m_CollisionType = type;
		
		ResizeShape();
		
	}
	
	void ResizeShape()
	{
		
		switch( m_CollisionType )
		{
			case CBOX_TRIANGLE:
				m_Points.resize( 3 );
				m_Axis.resize( 3 );
				break;
			case CBOX_QUAD:
				m_Points.resize( 4 );
				m_Axis.resize( 2 );
				break;
				
		}
	
	}
	
	void CalculateSATPoints()
	{
		
		switch( m_CollisionType )
		{
			
			case CBOX_QUAD:
				m_Axis[0].SetXY( 0, 1 );
				m_Axis[0].Normalize();
				m_Axis[1].SetXY( -1, 0 );
				m_Axis[1].Normalize();
				
				m_TestPoints.clear();
				m_TestPoints.push_back( m_Points[0] );
				m_TestPoints.push_back( m_Points[2] );
				
				break;
			case CBOX_TRIANGLE:
				m_TestPoints.clear();
				
				for( int j = 0; j < 3; j++ )
				{
					
					m_Axis[j] = m_Points[(j + 1)>=3?0:j+1] - m_Points[j];
					m_Axis[j].SetXY( -m_Axis[j].y, m_Axis[j].x );
					m_Axis[j].Normalize();
					
					m_TestPoints.push_back( m_Points[j] );
				}
				break;
				
		}
		
	}
	
	inline void SetSpecialMapIndex( int i )
	{
		
		m_SpecialMapIndex = i;
		
	}
	
	inline int GetSpecialMapIndex()
	{
		
		return m_SpecialMapIndex;
		
	}
	
	inline bool Enabled()
	{
		
		return m_bEnabled;
		
	}
	
	inline void SetEnabled( bool b )
	{
		
		m_bEnabled = b;
		
	}
	
	inline bool ContainsPoint( const Vector2D & p )
	{
		
		double x1 = m_Points[0].x;
		double y1 = m_Points[0].y;
		double x2 = m_Points[2].x;
		double y2 = m_Points[2].y;
		
		if( p.x >= x1 && p.x <= x2 &&
			p.y >= y1 && p.y <= y2 )
			return true;
		
		return false;
		
	}
	
	inline void SetPos( double x, double y )
	{
		
		m_Pos.SetXY( x, y );
		
	}
	
	inline const Vector2D & GetPoint( int i ) 
	{
		
		return m_Points[i];
		
	}
	
	void SetPoint( int i, double x, double y )
	{
		
		m_Points[i].SetXY( x, y );
		
	}
	
	inline void FitBox2( float x, float y, float width, float height, float mul = 1.0f, float ymul = 1.0f )
	{
		
		float x1 = width * x;
		float y1 = height * y;
		float x2 = width * ( x + mul );
		float y2 = height * ( y + ymul );
		
		m_Points[0].SetXY( x1, y1 );
		m_Points[1].SetXY( x2, y1 );
		m_Points[2].SetXY( x2, y2 );
		m_Points[3].SetXY( x1, y2 );
		
		CalculateSATPoints();
		
	}
	
	inline void FitBox( int width, int height, float mul = 1.0f, float ymul = 1.0f )
	{
		
		float halfwidth = width * .5f;
		float halfheight = height * .5f;
	
		double x1 = halfwidth * ( 1.0f - mul );
		double y1 = halfheight * ( 1.0f - ymul );
		double x2 = halfwidth + halfwidth * mul;
		double y2 = halfheight + halfheight * ymul;
		
		m_Points[0].SetXY( x1, y1 );
		m_Points[1].SetXY( x2, y1 );
		m_Points[2].SetXY( x2, y2 );
		m_Points[3].SetXY( x1, y2 );
		
		CalculateSATPoints();
		
	}
	
	inline void ShiftToBottom( int height )
	{
		
		float h = m_Points[2].y - m_Points[0].y;
		
		float newy = height - h;
		
		m_Points[0].y = m_Points[1].y = newy;
		m_Points[2].y = m_Points[3].y = newy + h;
		
		CalculateSATPoints();
		
	}
	
	bool SATQuadCollision( CCollisionBox *, CCollisionData & );
	
	inline void Center()
	{
		
		double x1 = m_Points[0].x;
		double y1 = m_Points[0].y;
		double x2 = m_Points[2].x;
		double y2 = m_Points[2].y;
		
		m_Center.SetXY( ( x2 - x1 ) * 0.5, ( y2 - y1 ) * 0.5 );
		
	}
	
	inline const Vector2D & GetCenter()
	{
		
		return m_Center;
		
	}

	
	//For turning your collision box to a sloped one
	inline void RemovePoint( int i )
	{
	
		m_Points[i].SetXY( -1, -1 );
		
	}
	
	bool TestQuadCollision( CCollisionBox * );
	bool IsCollidingWith( CCollisionBox *, CCollisionData & );

};

class CCollisionData
{
	
public:
	
	bool bCollision;
	CCollisionBox * colBox1, * colBox2;
	double minTransDist;
	Vector2D minTransAxis;
	
	int recID;

};

class CCollisionReport
{
	
public:

	
	int count()
	{
		
		return m_CollisionData.size();
		
	}
	
	CCollisionData & operator[]( int i )
	{
		
		return m_CollisionData.at( i );
		
	}
	
	std::vector< CCollisionData > m_CollisionData;
	
	int m_MaxData;
	
	CCollisionReport()
	{
		
		m_MaxData = 99;
		
	}
	
};


/*

//Used for collision testing.
class CTestCollisionBox
{
	
	Vector2D m_Points[4];
	Vector2D m_OriginPoints[4];
	
public:
	
	inline void SetSpace( double x, double y, CCollisionBox & b )
	{
		
		for( int j = 0; j < 4; j++ )
		{
			
		
			m_OriginPoints[j].SetXY( b.GetPoint( j ).x + x, b.GetPoint( j ).y + y );
			m_Points[j].SetXY( b.GetPoint( j ).x + x, b.GetPoint( j ).y + y );
			
			
		}
		
	}
	
	
	inline void RotateFromWorldPoint( double rad, double x, double y )
	{
	
		for( int j = 0; j < 4; j++ )
		{
			
			double ox = m_OriginPoints[j].x;
			double oy = m_OriginPoints[j].y;

			float s = sin( rad );
			float c = cos( rad );
			
			ox -= x;
			oy -= y;
			
			double rx = ox * c - oy * s;
			double ry = ox * s + oy * c;
			
			m_Points[j].x = rx + x;
			m_Points[j].y = ry + y;
			
			
		}
		

	}
	
	inline void TestDraw()
	{
	
		glPushMatrix();
		
		glBegin( GL_QUADS );
		
			for( int j = 0; j < 4; j++ )
			{
			
				glVertex2f( m_Points[j].x, m_Points[j].y );
				
			}
		
		glEnd();
		
		glPopMatrix();
		
	}
	
};
*/

#endif
